package com.jsy.common.util.gitIncrement;

import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * SVN 增量包导出工具类
 * 通过调用 SVN 命令行工具实现版本间差异文件的导出
 *windows导出增量包
 * for /L %i in (1,1,56) do (
 *     svn export -r %i https://172.21.152.127:8443/svn/wizbank ../patch/%i
 * )
 */
public class SVNIncrementalExporter {
    private String svnPath;       // SVN 命令路径
    private String repoUrl;       // SVN 仓库 URL
    private String username;      // SVN 用户名
    private String password;      // SVN 密码
    private boolean verbose;      // 是否输出详细日志

    /**
     * 构造函数
     * @param repoUrl SVN 仓库 URL
     * @param username SVN 用户名
     * @param password SVN 密码
     */
    public SVNIncrementalExporter(String repoUrl, String username, String password) {
        this.svnPath = "svn";     // 默认使用系统路径中的 SVN 命令
        this.repoUrl = repoUrl;
        this.username = username;
        this.password = password;
        this.verbose = false;     // 默认不输出详细日志
    }

    /**
     * 设置 SVN 命令的完整路径
     * @param svnPath SVN 命令路径
     */
    public void setSvnPath(String svnPath) {
        this.svnPath = svnPath;
    }

    /**
     * 设置是否输出详细日志
     * @param verbose true 表示输出详细日志，false 表示不输出
     */
    public void setVerbose(boolean verbose) {
        this.verbose = verbose;
    }

    /**
     * 获取两个版本之间的变更文件列表
     * @param fromRevision 起始版本号
     * @param toRevision 结束版本号
     * @return 变更文件路径列表
     * @throws IOException 如果执行命令过程中发生 IO 异常
     * @throws InterruptedException 如果执行命令过程中线程被中断
     */
    public List<String> getChangedFiles(long fromRevision, long toRevision) throws IOException, InterruptedException {
        log("获取版本 " + fromRevision + " 到 " + toRevision + " 之间的变更文件列表...");
        
        // 构建 SVN 命令：svn diff --summarize -r 100:200
        List<String> command = buildSvnCommand("diff", "--summarize", "-r", fromRevision + ":" + toRevision, repoUrl);
        
        // 执行命令并获取输出
        ProcessResult result = executeCommand(command);
        
        if (result.exitCode != 0) {
            throw new IOException("获取变更文件列表失败，错误码: " + result.exitCode + "\n" + result.errorOutput);
        }
        
        List<String> changedFiles = new ArrayList<>();
        for (String line : result.output) {
            // 解析输出格式: [AMDR] 路径
            if (line.length() > 2) {
                char action = line.charAt(0);
                // 只处理添加(A)、修改(M)和替换(R)的文件，忽略删除(D)的文件
                if (action == 'A' || action == 'M' || action == 'R') {
                    String path = line.substring(2).trim();
                    changedFiles.add(path);
                }
            }
        }
        
        log("找到 " + changedFiles.size() + " 个变更文件");
        return changedFiles;
    }

    /**
     * 导出变更文件到指定目录
     * @param changedFiles 变更文件列表
     * @param revision 导出的版本号
     * @param targetDir 目标目录
     * @throws IOException 如果执行命令过程中发生 IO 异常
     * @throws InterruptedException 如果执行命令过程中线程被中断
     */
    public void exportChangedFiles(List<String> changedFiles, long revision, Path targetDir) throws IOException, InterruptedException {
        log("开始导出 " + changedFiles.size() + " 个变更文件到目录: " + targetDir);
        
        // 确保目标目录存在
        Files.createDirectories(targetDir);
        
        int successCount = 0;
        int failedCount = 0;
        
        // 遍历变更文件并导出
        for (String filePath : changedFiles) {
            Path destPath = targetDir.resolve(filePath);
            
            // 创建父目录
            Files.createDirectories(destPath.getParent());
            
            // 构建 SVN 命令：svn export -r 200 URL 目标路径
            List<String> command = buildSvnCommand(
                "export", "-r", String.valueOf(revision),
                repoUrl + "/" + filePath,
                destPath.toString()
            );
            
            // 执行导出命令
            ProcessResult result = executeCommand(command);
            
            if (result.exitCode != 0) {
                failedCount++;
                logError("导出失败: " + filePath + "，错误码: " + result.exitCode + "\n" + result.errorOutput);
            } else {
                successCount++;
                log("导出成功: " + filePath);
            }
        }
        
        log("导出完成: 成功 " + successCount + " 个，失败 " + failedCount + " 个");
    }

    /**
     * 生成补丁文件
     * @param fromRevision 起始版本号
     * @param toRevision 结束版本号
     * @param patchFile 补丁文件路径
     * @throws IOException 如果执行命令过程中发生 IO 异常
     * @throws InterruptedException 如果执行命令过程中线程被中断
     */
    public void generatePatchFile(long fromRevision, long toRevision, Path patchFile) throws IOException, InterruptedException {
        log("生成版本 " + fromRevision + " 到 " + toRevision + " 的补丁文件: " + patchFile);
        
        // 构建 SVN 命令：svn diff -r 100:200 > patchfile
        List<String> command = buildSvnCommand("diff", "-r", fromRevision + ":" + toRevision, repoUrl);
        
        // 执行命令并将输出写入补丁文件
        ProcessBuilder pb = new ProcessBuilder(command);
        pb.redirectOutput(patchFile.toFile());
        
        Process process = pb.start();
        
        // 读取错误输出
        StringBuilder errorMsg = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                errorMsg.append(line).append("\n");
            }
        }
        
        // 等待命令执行完成
        int exitCode = process.waitFor();
        
        if (exitCode != 0) {
            throw new IOException("生成补丁文件失败，错误码: " + exitCode + "\n" + errorMsg);
        }
        
        log("补丁文件生成完成，大小: " + Files.size(patchFile) + " 字节");
    }

    /**
     * 过滤特定类型的文件
     * @param files 文件列表
     * @param extensions 文件扩展名列表（如 ".java", ".xml"）
     * @return 过滤后的文件列表
     */
    public static List<String> filterFilesByExtensions(List<String> files, String... extensions) {
        Set<String> extSet = new HashSet<>(Arrays.asList(extensions));
        return files.stream()
            .filter(file -> extSet.stream().anyMatch(file::endsWith))
            .collect(Collectors.toList());
    }

    /**
     * 构建 SVN 命令列表（包含认证信息）
     * @param args 命令参数
     * @return 完整的命令列表
     */
    private List<String> buildSvnCommand(String... args) {
        List<String> command = new ArrayList<>();
        command.add(svnPath);
        
        // 添加命令参数
        command.addAll(Arrays.asList(args));
        
        // 添加认证信息
        if (username != null && !username.isEmpty()) {
            command.add("--username");
            command.add(username);
            command.add("--password");
            command.add(password);
            command.add("--no-auth-cache");
        }
        
        return command;
    }

    /**
     * 执行外部命令并返回结果
     * @param command 命令列表
     * @return 命令执行结果
     * @throws IOException 如果执行命令过程中发生 IO 异常
     * @throws InterruptedException 如果执行命令过程中线程被中断
     */
    private ProcessResult executeCommand(List<String> command) throws IOException, InterruptedException {
        if (verbose) {
            log("执行命令: " + String.join(" ", command));
        }
        
        ProcessBuilder pb = new ProcessBuilder(command);
        Process process = pb.start();
        
        // 读取标准输出
        List<String> output = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                output.add(line);
            }
        }
        
        // 读取错误输出
        StringBuilder errorMsg = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                errorMsg.append(line).append("\n");
            }
        }
        
        // 等待命令执行完成
        int exitCode = process.waitFor();
        
        return new ProcessResult(exitCode, output, errorMsg.toString());
    }

    /**
     * 记录日志（如果启用了详细模式）
     * @param message 日志消息
     */
    private void log(String message) {
        if (verbose) {
            System.out.println("[SVNExporter] " + message);
        }
    }

    /**
     * 记录错误日志
     * @param message 错误消息
     */
    private void logError(String message) {
        System.err.println("[SVNExporter] ERROR: " + message);
    }

    /**
     * 命令执行结果内部类
     */
    private static class ProcessResult {
        private final int exitCode;
        private final List<String> output;
        private final String errorOutput;

        public ProcessResult(int exitCode, List<String> output, String errorOutput) {
            this.exitCode = exitCode;
            this.output = output;
            this.errorOutput = errorOutput;
        }
    }

    // 示例用法
    public static void main(String[] args) {
        String repoUrl = "https://svn.example.com/repo/trunk";
        String username = "your_username";
        String password = "your_password";
        long fromRevision = 100;
        long toRevision = 200;
        Path exportDir = Paths.get("svn_incremental_export");
        Path patchFile = Paths.get("svn_changes.patch");
        
        try {
            SVNIncrementalExporter exporter = new SVNIncrementalExporter(repoUrl, username, password);
            exporter.setVerbose(true);  // 启用详细日志
            
            // 获取变更文件列表
            List<String> changedFiles = exporter.getChangedFiles(fromRevision, toRevision);
            
            // 过滤特定类型的文件（可选）
            List<String> filteredFiles = filterFilesByExtensions(
                changedFiles, ".java", ".xml", ".properties", ".class"
            );
            
            // 导出变更文件
            exporter.exportChangedFiles(filteredFiles, toRevision, exportDir);
            
            // 生成补丁文件
            exporter.generatePatchFile(fromRevision, toRevision, patchFile);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}